gecko-dev/dom/bindings/BindingUtils.h
2018-04-04 15:32:19 -04:00

3442 lines
105 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/Wrapper.h"
#include "js/Conversions.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Alignment.h"
#include "mozilla/Array.h"
#include "mozilla/Assertions.h"
#include "mozilla/CycleCollectedJSContext.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/RootedDictionary.h"
#include "mozilla/SegmentedVector.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/Likely.h"
#include "mozilla/MemoryReporting.h"
#include "nsAutoPtr.h"
#include "nsIDocument.h"
#include "nsIGlobalObject.h"
#include "nsIXPConnect.h"
#include "nsJSUtils.h"
#include "nsISupportsImpl.h"
#include "qsObjectHelper.h"
#include "xpcpublic.h"
#include "nsIVariant.h"
#include "mozilla/dom/FakeString.h"
#include "nsWrapperCacheInlines.h"
class nsGenericHTMLElement;
class nsIJSID;
namespace mozilla {
enum UseCounter : int16_t;
namespace dom {
class CustomElementReactionsStack;
class MessageManagerGlobal;
template<typename KeyType, typename ValueType> class Record;
nsresult
UnwrapArgImpl(JSContext* cx, JS::Handle<JSObject*> src, const nsIID& iid,
void** ppArg);
nsresult
UnwrapWindowProxyImpl(JSContext* cx, JS::Handle<JSObject*> src,
nsPIDOMWindowOuter** 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));
}
template <>
inline nsresult
UnwrapArg<nsPIDOMWindowOuter>(JSContext* cx, JS::Handle<JSObject*> src,
nsPIDOMWindowOuter** ppArg)
{
return UnwrapWindowProxyImpl(cx, src, ppArg);
}
bool
ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
bool aSecurityError, const char* aInterfaceName);
bool
ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
bool aSecurityError, prototypes::ID aProtoId);
// Returns true if the JSClass is used for DOM objects.
inline bool
IsDOMClass(const JSClass* clasp)
{
return clasp->flags & JSCLASS_IS_DOMJSCLASS;
}
inline bool
IsDOMClass(const js::Class* clasp)
{
return IsDOMClass(Jsvalify(clasp));
}
// Return true if the JSClass is used for non-proxy DOM objects.
inline bool
IsNonProxyDOMClass(const js::Class* clasp)
{
return IsDOMClass(clasp) && !clasp->isProxy();
}
inline bool
IsNonProxyDOMClass(const JSClass* clasp)
{
return IsNonProxyDOMClass(js::Valueify(clasp));
}
// 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;
}
inline bool
IsDOMIfaceAndProtoClass(const js::Class* clasp)
{
return IsDOMIfaceAndProtoClass(Jsvalify(clasp));
}
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 js::Class* 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*.
#define UNWRAP_OBJECT(Interface, obj, value) \
mozilla::dom::UnwrapObject<mozilla::dom::prototypes::id::Interface, \
mozilla::dom::Interface##Binding::NativeType>(obj, value)
// 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.
namespace binding_detail {
template <class T, bool mayBeWrapper, typename U, typename V>
MOZ_ALWAYS_INLINE nsresult
UnwrapObjectInternal(V& obj, U& value, prototypes::ID protoID,
uint32_t protoDepth)
{
/* 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)) {
/* Not a DOM object, not a wrapper, just bail */
return NS_ERROR_XPC_BAD_CONVERT_JS;
}
JSObject* unwrappedObj =
js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
if (!unwrappedObj) {
return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
}
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);
if (NS_SUCCEEDED(rv)) {
// 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>
MOZ_ALWAYS_INLINE nsresult
UnwrapObject(JS::MutableHandle<JSObject*> obj, U& value)
{
binding_detail::MutableObjectHandleWrapper wrapper(obj);
return binding_detail::UnwrapObjectInternal<T, true>(
wrapper, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth);
}
template<prototypes::ID PrototypeID, class T, typename U>
MOZ_ALWAYS_INLINE nsresult
UnwrapObject(JS::MutableHandle<JS::Value> obj, U& value)
{
MOZ_ASSERT(obj.isObject());
binding_detail::MutableValueHandleWrapper wrapper(obj);
return binding_detail::UnwrapObjectInternal<T, true>(
wrapper, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth);
}
// UnwrapObject overloads that ensure we have a strong ref to keep it alive.
template<prototypes::ID PrototypeID, class T, typename U>
MOZ_ALWAYS_INLINE nsresult
UnwrapObject(JSObject* obj, RefPtr<U>& value)
{
return binding_detail::UnwrapObjectInternal<T, true>(
obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth);
}
template<prototypes::ID PrototypeID, class T, typename U>
MOZ_ALWAYS_INLINE nsresult
UnwrapObject(JSObject* obj, nsCOMPtr<U>& value)
{
return binding_detail::UnwrapObjectInternal<T, true>(
obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth);
}
template<prototypes::ID PrototypeID, class T, typename U>
MOZ_ALWAYS_INLINE nsresult
UnwrapObject(JSObject* obj, OwningNonNull<U>& value)
{
return binding_detail::UnwrapObjectInternal<T, true>(
obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth);
}
// An UnwrapObject overload that just calls one of the JSObject* ones.
template<prototypes::ID PrototypeID, class T, typename U>
MOZ_ALWAYS_INLINE nsresult
UnwrapObject(JS::Handle<JS::Value> obj, U& value)
{
MOZ_ASSERT(obj.isObject());
return UnwrapObject<PrototypeID, T>(&obj.toObject(), value);
}
template<prototypes::ID PrototypeID, class T>
MOZ_ALWAYS_INLINE bool
IsInstanceOf(JSObject* obj)
{
void* ignored;
nsresult unwrapped = binding_detail::UnwrapObjectInternal<T, true>(
obj, ignored, PrototypeID, PrototypeTraits<PrototypeID>::Depth);
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);
}
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)
{}
void onChild(const JS::GCCellPtr&) override {
// We don't do anything here, we only want to verify that
// TraceProtoAndIfaceCache was called.
}
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;
};
/*
* 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.
* 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] or
* [PrimaryGlobal] interface, and hence shouldn't define properties on
* the prototype object.
*
* 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.
*/
void
CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
JS::Handle<JSObject*> protoProto,
const js::Class* protoClass, JS::Heap<JSObject*>* protoCache,
JS::Handle<JSObject*> interfaceProto,
const js::Class* 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);
/**
* Define the properties (regular and chrome-only) on obj.
*
* obj the object to instal 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(!IsBaseOf<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=IsBaseOf<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
MOZ_ALWAYS_INLINE bool
CouldBeDOMBinding(void*)
{
return true;
}
MOZ_ALWAYS_INLINE bool
CouldBeDOMBinding(nsWrapperCache* aCache)
{
return aCache->IsDOMBinding();
}
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;
}
// 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 even then we might need to wrap
// objects specially. Check for that.
if (IsDOMObject(obj)) {
return TryToOuterize(rval);
}
// It's not a WebIDL object, so it's OK to just leave it as-is: only WebIDL
// objects (specifically only windows) require outerization.
return true;
}
// 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 or XPConnect objects
MOZ_ALWAYS_INLINE
bool
MaybeWrapNonDOMObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
{
MOZ_ASSERT(rval.isObject());
MOZ_ASSERT(!GetDOMClass(&rval.toObject()));
MOZ_ASSERT(!(js::GetObjectClass(&rval.toObject())->flags &
JSCLASS_PRIVATE_IS_NSISUPPORTS));
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);
}
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 =
IsBaseOf<nsGlobalWindowInner, T>::value ||
IsBaseOf<nsGlobalWindowOuter, T>::value ||
IsSame<EventTarget, T>::value;
};
#ifdef DEBUG
template<typename T, bool isISupports=IsBaseOf<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));
// We can get rid of this when we remove support for
// nsWrapperCache::SetIsNotDOMBinding.
bool couldBeDOMBinding = CouldBeDOMBinding(value);
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 {
// Inline this here while we have non-dom objects in wrapper caches.
if (!couldBeDOMBinding) {
return false;
}
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 (IsBaseOf<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, (IsBaseOf<nsISupports, T>::value));
MOZ_ASSERT(CheckWrapperCacheCast<T>::Check());
}
#endif
rval.set(JS::ObjectValue(*obj));
bool sameCompartment =
js::GetObjectCompartment(obj) == js::GetContextCompartment(cx);
if (sameCompartment && couldBeDOMBinding) {
return TypeNeedsOuterization<T>::value ? TryToOuterize(rval) : true;
}
if (wrapBehavior == eDontWrapIntoContextCompartment) {
if (TypeNeedsOuterization<T>::value) {
JSAutoCompartment ac(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 scope.
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 compartment of the underlying object of "scope"
JS::Rooted<JSObject*> obj(cx);
{
// scope for the JSAutoCompartment so that we restore the compartment
// before we call JS_WrapValue.
Maybe<JSAutoCompartment> ac;
// 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)) {
scope = js::CheckedUnwrap(scope, /* stopAtWindowProxy = */ false);
if (!scope)
return false;
ac.emplace(cx, scope);
if (!JS_WrapObject(cx, &proto)) {
return false;
}
}
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 JSContext, a scope, and a boolean outparam 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 compartment of the underlying object of "scope"
JS::Rooted<JSObject*> obj(cx);
{
// scope for the JSAutoCompartment so that we restore the compartment
// before we call JS_WrapValue.
Maybe<JSAutoCompartment> ac;
// 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)) {
scope = js::CheckedUnwrap(scope, /* stopAtWindowProxy = */ false);
if (!scope)
return false;
ac.emplace(cx, scope);
if (!JS_WrapObject(cx, &proto)) {
return false;
}
}
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);
}
// 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
NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
JS::Handle<JSObject*> aScope,
JS::MutableHandle<JS::Value> aRetval,
xpcObjectHelper& aHelper,
const nsIID* aIID,
bool aAllowNativeWrapper);
/**
* A method to handle new-binding wrap failure, by possibly falling back to
* wrapping as a non-new-binding object.
*/
template <class T>
MOZ_ALWAYS_INLINE bool
HandleNewBindingWrappingFailure(JSContext* cx, JS::Handle<JSObject*> scope,
T* value, JS::MutableHandle<JS::Value> rval)
{
if (JS_IsExceptionPending(cx)) {
return false;
}
qsObjectHelper helper(value, GetWrapperCache(value));
return NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval,
helper, nullptr, true);
}
// Helper for calling HandleNewBindingWrappingFailure with smart pointers
// (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
template <class T, bool isSmartPtr=IsSmartPtr<T>::value>
struct HandleNewBindingWrappingFailureHelper
{
static inline bool Wrap(JSContext* cx, JS::Handle<JSObject*> scope,
const T& value, JS::MutableHandle<JS::Value> rval)
{
return HandleNewBindingWrappingFailure(cx, scope, value.get(), rval);
}
};
template <class T>
struct HandleNewBindingWrappingFailureHelper<T, false>
{
static inline bool Wrap(JSContext* cx, JS::Handle<JSObject*> scope, T& value,
JS::MutableHandle<JS::Value> rval)
{
return HandleNewBindingWrappingFailure(cx, scope, &value, rval);
}
};
template<class T>
inline bool
HandleNewBindingWrappingFailure(JSContext* cx, JS::Handle<JSObject*> scope,
T& value, JS::MutableHandle<JS::Value> rval)
{
return HandleNewBindingWrappingFailureHelper<T>::Wrap(cx, scope, value, rval);
}
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)
{
JSAutoByteString deflated;
if (!deflated.encodeUtf8(cx, str)) {
return false;
}
return ThrowErrorMessage(cx, MSG_INVALID_ENUM_VALUE, sourceDescription,
deflated.ptr(), 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 bool
GetUseXBLScope(T* aParentObject)
{
return false;
}
inline bool
GetUseXBLScope(const ParentObject& aParentObject)
{
return aParentObject.mUseXBLScope;
}
template<class T>
inline void
ClearWrapper(T* p, nsWrapperCache* cache, JSObject* obj)
{
JS::AutoAssertGCCallback inCallback;
cache->ClearWrapper(obj);
}
template<class T>
inline void
ClearWrapper(T* p, void*, JSObject* obj)
{
JS::AutoAssertGCCallback inCallback;
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(JSObject* obj);
// Can only be called with a DOM JSClass.
bool
InstanceClassHasProtoAtDepth(const js::Class* 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;
qsObjectHelper helper(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;
}
// 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)
{
qsObjectHelper helper(ToSupports(p), cache);
JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
JS::Rooted<JS::Value> v(cx);
return XPCOMObjectToJsval(cx, scope, helper, nullptr, false, &v) ?
v.toObjectOrNull() :
nullptr;
}
// Fallback for when our parent is not a WebIDL binding object.
template<typename T, bool isISupports=IsBaseOf<nsISupports, T>::value>
struct WrapNativeFallback
{
static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
{
return nullptr;
}
};
// Fallback for when our parent is not a WebIDL binding object but _is_ an
// nsISupports object.
template<typename T >
struct WrapNativeFallback<T, true >
{
static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
{
return WrapNativeISupports(cx, parent, cache);
}
};
// Wrapping of our native parent, for cases when it's a WebIDL object (though
// possibly preffed off).
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.
MOZ_ASSERT(JS::ObjectIsNotGray(obj));
return obj;
}
// Inline this here while we have non-dom objects in wrapper caches.
if (!CouldBeDOMBinding(parent)) {
// WrapNativeFallback never returns a gray thing.
obj = WrapNativeFallback<T>::Wrap(cx, parent, cache);
MOZ_ASSERT(JS::ObjectIsNotGray(obj));
} else {
// WrapObject never returns a gray thing.
obj = parent->WrapObject(cx, nullptr);
MOZ_ASSERT(JS::ObjectIsNotGray(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
MOZ_ASSERT(JS::ObjectIsNotGray(obj));
return obj;
}
obj = WrapNativeISupports(cx, parent, cache);
MOZ_ASSERT(JS::ObjectIsNotGray(obj));
return obj;
}
};
// Finding the associated global for an object.
template<typename T>
static inline JSObject*
FindAssociatedGlobal(JSContext* cx, T* p, nsWrapperCache* cache,
bool useXBLScope = false)
{
if (!p) {
return JS::CurrentGlobalOrNull(cx);
}
JSObject* obj = WrapNativeHelper<T>::Wrap(cx, p, cache);
if (!obj) {
return nullptr;
}
MOZ_ASSERT(JS::ObjectIsNotGray(obj));
obj = js::GetGlobalForObjectCrossCompartment(obj);
if (!useXBLScope) {
return obj;
}
// If useXBLScope is true, it means that the canonical reflector for this
// native object should live in the content XBL scope. Note that we never put
// anonymous content inside an add-on scope.
if (xpc::IsInContentXBLScope(obj)) {
return obj;
}
JS::Rooted<JSObject*> rootedObj(cx, obj);
JSObject* xblScope = xpc::GetXBLScope(cx, rootedObj);
MOZ_ASSERT_IF(xblScope, JS_IsGlobalObject(xblScope));
MOZ_ASSERT(JS::ObjectIsNotGray(xblScope));
return xblScope;
}
// 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), GetUseXBLScope(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) {
return nullptr;
}
MOZ_ASSERT(JS_IsGlobalObject(global));
// This object could be gray if the nsIGlobalObject is the only thing keeping
// it alive.
JS::ExposeObjectToActiveJS(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);
bool
QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp);
template <class T>
struct
WantsQueryInterface
{
static_assert(IsBaseOf<nsISupports, T>::value,
"QueryInterface can't work without an nsISupports.");
static bool Enabled(JSContext* aCx, JSObject* aGlobal)
{
return NS_IsMainThread() && IsChromeOrXBL(aCx, aGlobal);
}
};
void
GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
nsWrapperCache* aCache, nsIJSID* aIID,
JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError);
template<class T>
void
GetInterface(JSContext* aCx, T* aThis, nsIJSID* 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::AutoIdVector& props);
enum StringificationBehavior {
eStringify,
eEmpty,
eNull
};
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);
}
void
NormalizeUSVString(nsAString& aString);
void
NormalizeUSVString(binding_detail::FakeString& 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;
}
NormalizeUSVString(result);
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, InfallibleTArray<T>& seq);
// Class used to trace sequences, with specializations for various
// sequence types.
template<typename T,
bool isDictionary=IsBaseOf<DictionaryBase, T>::value,
bool isTypedArray=IsBaseOf<AllTypedArraysBase, T>::value,
bool isOwningUnion=IsBaseOf<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, InfallibleTArray<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:
SequenceRooter(JSContext *aCx, FallibleTArray<T>* aSequence
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
mFallibleArray(aSequence),
mSequenceType(eFallibleArray)
{
}
SequenceRooter(JSContext *aCx, InfallibleTArray<T>* aSequence
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
mInfallibleArray(aSequence),
mSequenceType(eInfallibleArray)
{
}
SequenceRooter(JSContext *aCx, Nullable<nsTArray<T> >* aSequence
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: JS::CustomAutoRooter(aCx 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 {
InfallibleTArray<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:
RecordRooter(JSContext *aCx, Record<K, V>* aRecord
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
mRecord(aRecord),
mRecordType(eRecord)
{
}
RecordRooter(JSContext *aCx, Nullable<Record<K, V>>* aRecord
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: JS::CustomAutoRooter(aCx 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:
explicit RootedUnion(JSContext* 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:
explicit NullableRootedUnion(JSContext* 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
IdEquals(jsid id, const char* string)
{
return JSID_IS_STRING(id) &&
JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), string);
}
inline bool
AddStringToIDVector(JSContext* cx, JS::AutoIdVector& 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::AutoIdVector& 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::GetGlobalForObjectCrossCompartment(obj));
{
JSAutoCompartment ac(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(cx, obj)) {
MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
protop.set(JS::GetRealmFunctionPrototype(cx));
} else {
const js::Class* 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 js::ClassOps sBoringInterfaceObjectClassClassOps;
extern const js::ObjectOps sInterfaceObjectClassObjectOps;
inline bool
UseDOMXray(JSObject* obj)
{
const js::Class* 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 js::Class* 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;
}
inline
void NonNullHelper(NonNull<binding_detail::FakeString>& 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.
}
inline
void NonNullHelper(const NonNull<binding_detail::FakeString>& 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.
}
inline
void NonNullHelper(binding_detail::FakeString& 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.
}
MOZ_ALWAYS_INLINE
const nsAString& NonNullHelper(const binding_detail::FakeString& aArg)
{
return aArg;
}
// Reparent the wrapper of aObj to whatever its native now thinks its
// parent should be.
void
ReparentWrapper(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,
prototypes::ID prototypeID, int depth);
// 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);
already_AddRefed<nsIGlobalObject>
ConstructJSImplementation(const char* aContractId,
const GlobalObject& aGlobal,
JS::MutableHandle<JSObject*> aObject,
ErrorResult& 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);
}
template<class T, bool isISupports=IsBaseOf<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=IsBaseOf<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 js::Class* aClass,
const DOMProxyHandler* aHandler,
JS::Handle<JSObject*> aProto, T* aNative,
JS::Handle<JS::Value> aExpandoValue,
JS::MutableHandle<JSObject*> aReflector)
{
js::ProxyOptions options;
options.setClass(aClass);
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_updateMallocCounter(aCx, mallocBytes);
}
}
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_updateMallocCounter(aCx, mallocBytes);
}
}
void
InitializationSucceeded()
{
void* dummy;
mNative.forget(&dummy);
mReflector = nullptr;
}
private:
struct OwnedNative
{
// Make sure the native objects inherit from NonRefcountedDOMObject so
// that we log their ctor and dtor.
static_assert(IsBaseOf<NonRefcountedDOMObject, T>::value,
"Non-refcounted objects with DOM bindings should inherit "
"from NonRefcountedDOMObject.");
OwnedNative&
operator=(T* 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(void**)
{
}
};
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 || !IsBaseOf<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=IsBaseOf<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 or null if it
// doesn't. This also returns null for classes that don't inherit from
// nsISupports (QI should be used to get the participant for those).
template<class T, bool isISupports=IsBaseOf<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 firt 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::AutoIdVector& 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,
IsBaseOf<nsGlobalWindowInner, T>::value ||
IsBaseOf<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;
static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
};
nsresult
RegisterDOMNames();
// 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::CompartmentOptions& aOptions,
JSPrincipals* aPrincipal, bool aInitStandardClasses,
JS::MutableHandle<JSObject*> aGlobal)
{
aOptions.creationOptions().setTrace(CreateGlobalOptions<T>::TraceGlobal);
if (xpc::SharedMemoryEnabled()) {
aOptions.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
}
aGlobal.set(JS_NewGlobalObject(aCx, aClass, aPrincipal,
JS::DontFireOnNewGlobalHook, aOptions));
if (!aGlobal) {
NS_WARNING("Failed to create global");
return false;
}
JSAutoCompartment ac(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_InitStandardClasses(aCx, aGlobal)) {
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");
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);
}
};
bool
GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp);
bool
GenericPromiseReturningBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp);
bool
GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp);
bool
GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp);
bool
GenericPromiseReturningBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp);
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>
inline bool
WrappedJSToDictionary(JSContext* aCx, nsISupports* aObject, T& aDictionary)
{
nsCOMPtr<nsIXPConnectWrappedJS> wrappedObj = do_QueryInterface(aObject);
if (!wrappedObj) {
return false;
}
JS::Rooted<JSObject*> obj(aCx, wrappedObj->GetJSObject());
if (!obj) {
return false;
}
JSAutoCompartment ac(aCx, obj);
JS::Rooted<JS::Value> v(aCx, JS::ObjectValue(*obj));
return aDictionary.Init(aCx, v);
}
template<class T>
inline bool
WrappedJSToDictionary(nsISupports* aObject, T& aDictionary)
{
nsCOMPtr<nsIXPConnectWrappedJS> wrappedObj = do_QueryInterface(aObject);
NS_ENSURE_TRUE(wrappedObj, false);
JS::Rooted<JSObject*> obj(RootingCx(), wrappedObj->GetJSObject());
NS_ENSURE_TRUE(obj, false);
nsIGlobalObject* global = xpc::NativeGlobal(obj);
NS_ENSURE_TRUE(global, false);
// we need this AutoEntryScript here because the spec requires us to execute
// getters when parsing a dictionary
AutoEntryScript aes(global, "WebIDL dictionary creation");
JS::Rooted<JS::Value> v(aes.cx(), JS::ObjectValue(*obj));
return aDictionary.Init(aes.cx(), v);
}
template<class T, class S>
inline RefPtr<T>
StrongOrRawPtr(already_AddRefed<S>&& aPtr)
{
return 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. Null is returned if the default prototype should be used.
bool
GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
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
SetDocumentAndPageUseCounter(JSObject* aObject, UseCounter aUseCounter);
// Warnings
void
DeprecationWarning(JSContext* aCx, JSObject* aObject,
nsIDocument::DeprecatedOperations aOperation);
void
DeprecationWarning(const GlobalObject& aGlobal,
nsIDocument::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);
} // namespace binding_detail
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_BindingUtils_h__ */