зеркало из https://github.com/mozilla/gecko-dev.git
548 строки
16 KiB
C++
548 строки
16 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/. */
|
|
|
|
/**
|
|
* A header for declaring various things that binding implementation headers
|
|
* might need. The idea is to make binding implementation headers safe to
|
|
* include anywhere without running into include hell like we do with
|
|
* BindingUtils.h
|
|
*/
|
|
#ifndef mozilla_dom_BindingDeclarations_h__
|
|
#define mozilla_dom_BindingDeclarations_h__
|
|
|
|
#include "js/RootingAPI.h"
|
|
#include "js/TypeDecls.h"
|
|
|
|
#include "mozilla/Maybe.h"
|
|
|
|
#include "mozilla/dom/DOMString.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsString.h"
|
|
#include "nsTArray.h"
|
|
|
|
#include <type_traits>
|
|
|
|
#include "js/Value.h"
|
|
#include "mozilla/RootedOwningNonNull.h"
|
|
#include "mozilla/RootedRefPtr.h"
|
|
|
|
class nsIPrincipal;
|
|
class nsWrapperCache;
|
|
|
|
namespace mozilla {
|
|
|
|
class ErrorResult;
|
|
class OOMReporter;
|
|
class CopyableErrorResult;
|
|
|
|
namespace dom {
|
|
|
|
class BindingCallContext;
|
|
|
|
// Struct that serves as a base class for all dictionaries. Particularly useful
|
|
// so we can use std::is_base_of to detect dictionary template arguments.
|
|
struct DictionaryBase {
|
|
protected:
|
|
bool ParseJSON(JSContext* aCx, const nsAString& aJSON,
|
|
JS::MutableHandle<JS::Value> aVal);
|
|
|
|
bool StringifyToJSON(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
|
nsAString& aJSON) const;
|
|
|
|
// Struct used as a way to force a dictionary constructor to not init the
|
|
// dictionary (via constructing from a pointer to this class). We're putting
|
|
// it here so that all the dictionaries will have access to it, but outside
|
|
// code will not.
|
|
struct FastDictionaryInitializer {};
|
|
|
|
bool mIsAnyMemberPresent = false;
|
|
|
|
private:
|
|
// aString is expected to actually be an nsAString*. Should only be
|
|
// called from StringifyToJSON.
|
|
static bool AppendJSONToString(const char16_t* aJSONData,
|
|
uint32_t aDataLength, void* aString);
|
|
|
|
public:
|
|
bool IsAnyMemberPresent() const { return mIsAnyMemberPresent; }
|
|
};
|
|
|
|
template <typename T>
|
|
inline std::enable_if_t<std::is_base_of<DictionaryBase, T>::value, void>
|
|
ImplCycleCollectionUnlink(T& aDictionary) {
|
|
aDictionary.UnlinkForCC();
|
|
}
|
|
|
|
template <typename T>
|
|
inline std::enable_if_t<std::is_base_of<DictionaryBase, T>::value, void>
|
|
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
|
|
T& aDictionary, const char* aName,
|
|
uint32_t aFlags = 0) {
|
|
aDictionary.TraverseForCC(aCallback, aFlags);
|
|
}
|
|
|
|
template <typename T>
|
|
inline std::enable_if_t<std::is_base_of<DictionaryBase, T>::value, void>
|
|
ImplCycleCollectionUnlink(UniquePtr<T>& aDictionary) {
|
|
aDictionary.reset();
|
|
}
|
|
|
|
template <typename T>
|
|
inline std::enable_if_t<std::is_base_of<DictionaryBase, T>::value, void>
|
|
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
|
|
UniquePtr<T>& aDictionary, const char* aName,
|
|
uint32_t aFlags = 0) {
|
|
if (aDictionary) {
|
|
ImplCycleCollectionTraverse(aCallback, *aDictionary, aName, aFlags);
|
|
}
|
|
}
|
|
// Struct that serves as a base class for all typed arrays and array buffers and
|
|
// array buffer views. Particularly useful so we can use std::is_base_of to
|
|
// detect typed array/buffer/view template arguments.
|
|
struct AllTypedArraysBase {};
|
|
|
|
// Struct that serves as a base class for all owning unions.
|
|
// Particularly useful so we can use std::is_base_of to detect owning union
|
|
// template arguments.
|
|
struct AllOwningUnionBase {};
|
|
|
|
struct EnumEntry {
|
|
const char* value;
|
|
size_t length;
|
|
};
|
|
|
|
enum class CallerType : uint32_t;
|
|
|
|
class MOZ_STACK_CLASS GlobalObject {
|
|
public:
|
|
GlobalObject(JSContext* aCx, JSObject* aObject);
|
|
|
|
JSObject* Get() const { return mGlobalJSObject; }
|
|
|
|
nsISupports* GetAsSupports() const;
|
|
|
|
// The context that this returns is not guaranteed to be in the compartment of
|
|
// the object returned from Get(), in fact it's generally in the caller's
|
|
// compartment.
|
|
JSContext* Context() const { return mCx; }
|
|
|
|
bool Failed() const { return !Get(); }
|
|
|
|
// It returns the subjectPrincipal if called on the main-thread, otherwise
|
|
// a nullptr is returned.
|
|
nsIPrincipal* GetSubjectPrincipal() const;
|
|
|
|
// Get the caller type. Note that this needs to be called before anyone has
|
|
// had a chance to mess with the JSContext.
|
|
dom::CallerType CallerType() const;
|
|
|
|
protected:
|
|
JS::Rooted<JSObject*> mGlobalJSObject;
|
|
JSContext* mCx;
|
|
mutable nsISupports* MOZ_UNSAFE_REF(
|
|
"Valid because GlobalObject is a stack "
|
|
"class, and mGlobalObject points to the "
|
|
"global, so it won't be destroyed as long "
|
|
"as GlobalObject lives on the stack") mGlobalObject;
|
|
};
|
|
|
|
// Class for representing optional arguments.
|
|
template <typename T, typename InternalType>
|
|
class Optional_base {
|
|
public:
|
|
Optional_base() = default;
|
|
|
|
Optional_base(Optional_base&&) = default;
|
|
Optional_base& operator=(Optional_base&&) = default;
|
|
|
|
explicit Optional_base(const T& aValue) { mImpl.emplace(aValue); }
|
|
explicit Optional_base(T&& aValue) { mImpl.emplace(std::move(aValue)); }
|
|
|
|
bool operator==(const Optional_base<T, InternalType>& aOther) const {
|
|
return mImpl == aOther.mImpl;
|
|
}
|
|
|
|
bool operator!=(const Optional_base<T, InternalType>& aOther) const {
|
|
return mImpl != aOther.mImpl;
|
|
}
|
|
|
|
template <typename T1, typename T2>
|
|
explicit Optional_base(const T1& aValue1, const T2& aValue2) {
|
|
mImpl.emplace(aValue1, aValue2);
|
|
}
|
|
|
|
bool WasPassed() const { return mImpl.isSome(); }
|
|
|
|
// Return InternalType here so we can work with it usefully.
|
|
template <typename... Args>
|
|
InternalType& Construct(Args&&... aArgs) {
|
|
mImpl.emplace(std::forward<Args>(aArgs)...);
|
|
return *mImpl;
|
|
}
|
|
|
|
void Reset() { mImpl.reset(); }
|
|
|
|
const T& Value() const { return *mImpl; }
|
|
|
|
// Return InternalType here so we can work with it usefully.
|
|
InternalType& Value() { return *mImpl; }
|
|
|
|
// And an explicit way to get the InternalType even if we're const.
|
|
const InternalType& InternalValue() const { return *mImpl; }
|
|
|
|
// If we ever decide to add conversion operators for optional arrays
|
|
// like the ones Nullable has, we'll need to ensure that Maybe<> has
|
|
// the boolean before the actual data.
|
|
|
|
private:
|
|
// Forbid copy-construction and assignment
|
|
Optional_base(const Optional_base& other) = delete;
|
|
const Optional_base& operator=(const Optional_base& other) = delete;
|
|
|
|
protected:
|
|
Maybe<InternalType> mImpl;
|
|
};
|
|
|
|
template <typename T>
|
|
class Optional : public Optional_base<T, T> {
|
|
public:
|
|
MOZ_ALLOW_TEMPORARY Optional() : Optional_base<T, T>() {}
|
|
|
|
explicit Optional(const T& aValue) : Optional_base<T, T>(aValue) {}
|
|
Optional(Optional&&) = default;
|
|
};
|
|
|
|
template <typename T>
|
|
class Optional<JS::Handle<T>>
|
|
: public Optional_base<JS::Handle<T>, JS::Rooted<T>> {
|
|
public:
|
|
MOZ_ALLOW_TEMPORARY Optional()
|
|
: Optional_base<JS::Handle<T>, JS::Rooted<T>>() {}
|
|
|
|
explicit Optional(JSContext* cx)
|
|
: Optional_base<JS::Handle<T>, JS::Rooted<T>>() {
|
|
this->Construct(cx);
|
|
}
|
|
|
|
Optional(JSContext* cx, const T& aValue)
|
|
: Optional_base<JS::Handle<T>, JS::Rooted<T>>(cx, aValue) {}
|
|
|
|
// Override the const Value() to return the right thing so we're not
|
|
// returning references to temporaries.
|
|
JS::Handle<T> Value() const { return *this->mImpl; }
|
|
|
|
// And we have to override the non-const one too, since we're
|
|
// shadowing the one on the superclass.
|
|
JS::Rooted<T>& Value() { return *this->mImpl; }
|
|
};
|
|
|
|
// A specialization of Optional for JSObject* to make sure that when someone
|
|
// calls Construct() on it we will pre-initialized the JSObject* to nullptr so
|
|
// it can be traced safely.
|
|
template <>
|
|
class Optional<JSObject*> : public Optional_base<JSObject*, JSObject*> {
|
|
public:
|
|
Optional() = default;
|
|
|
|
explicit Optional(JSObject* aValue)
|
|
: Optional_base<JSObject*, JSObject*>(aValue) {}
|
|
|
|
// Don't allow us to have an uninitialized JSObject*
|
|
JSObject*& Construct() {
|
|
// The Android compiler sucks and thinks we're trying to construct
|
|
// a JSObject* from an int if we don't cast here. :(
|
|
return Optional_base<JSObject*, JSObject*>::Construct(
|
|
static_cast<JSObject*>(nullptr));
|
|
}
|
|
|
|
template <class T1>
|
|
JSObject*& Construct(const T1& t1) {
|
|
return Optional_base<JSObject*, JSObject*>::Construct(t1);
|
|
}
|
|
};
|
|
|
|
// A specialization of Optional for JS::Value to make sure no one ever uses it.
|
|
template <>
|
|
class Optional<JS::Value> {
|
|
private:
|
|
Optional() = delete;
|
|
|
|
explicit Optional(const JS::Value& aValue) = delete;
|
|
};
|
|
|
|
// A specialization of Optional for NonNull that lets us get a T& from Value()
|
|
template <typename U>
|
|
class NonNull;
|
|
template <typename T>
|
|
class Optional<NonNull<T>> : public Optional_base<T, NonNull<T>> {
|
|
public:
|
|
// We want our Value to actually return a non-const reference, even
|
|
// if we're const. At least for things that are normally pointer
|
|
// types...
|
|
T& Value() const { return *this->mImpl->get(); }
|
|
|
|
// And we have to override the non-const one too, since we're
|
|
// shadowing the one on the superclass.
|
|
NonNull<T>& Value() { return *this->mImpl; }
|
|
};
|
|
|
|
// A specialization of Optional for OwningNonNull that lets us get a
|
|
// T& from Value()
|
|
template <typename T>
|
|
class Optional<OwningNonNull<T>> : public Optional_base<T, OwningNonNull<T>> {
|
|
public:
|
|
// We want our Value to actually return a non-const reference, even
|
|
// if we're const. At least for things that are normally pointer
|
|
// types...
|
|
T& Value() const { return *this->mImpl->get(); }
|
|
|
|
// And we have to override the non-const one too, since we're
|
|
// shadowing the one on the superclass.
|
|
OwningNonNull<T>& Value() { return *this->mImpl; }
|
|
};
|
|
|
|
// Specialization for strings.
|
|
// XXXbz we can't pull in FakeString here, because it depends on internal
|
|
// strings. So we just have to forward-declare it and reimplement its
|
|
// ToAStringPtr.
|
|
|
|
namespace binding_detail {
|
|
template <typename CharT>
|
|
struct FakeString;
|
|
} // namespace binding_detail
|
|
|
|
template <typename CharT>
|
|
class Optional<nsTSubstring<CharT>> {
|
|
using AString = nsTSubstring<CharT>;
|
|
|
|
public:
|
|
Optional() : mStr(nullptr) {}
|
|
|
|
bool WasPassed() const { return !!mStr; }
|
|
|
|
void operator=(const AString* str) {
|
|
MOZ_ASSERT(str);
|
|
mStr = str;
|
|
}
|
|
|
|
// If this code ever goes away, remove the comment pointing to it in the
|
|
// FakeString class in BindingUtils.h.
|
|
void operator=(const binding_detail::FakeString<CharT>* str) {
|
|
MOZ_ASSERT(str);
|
|
mStr = reinterpret_cast<const nsTString<CharT>*>(str);
|
|
}
|
|
|
|
const AString& Value() const {
|
|
MOZ_ASSERT(WasPassed());
|
|
return *mStr;
|
|
}
|
|
|
|
private:
|
|
// Forbid copy-construction and assignment
|
|
Optional(const Optional& other) = delete;
|
|
const Optional& operator=(const Optional& other) = delete;
|
|
|
|
const AString* mStr;
|
|
};
|
|
|
|
template <typename T>
|
|
inline void ImplCycleCollectionUnlink(Optional<T>& aField) {
|
|
if (aField.WasPassed()) {
|
|
ImplCycleCollectionUnlink(aField.Value());
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
inline void ImplCycleCollectionTraverse(
|
|
nsCycleCollectionTraversalCallback& aCallback, Optional<T>& aField,
|
|
const char* aName, uint32_t aFlags = 0) {
|
|
if (aField.WasPassed()) {
|
|
ImplCycleCollectionTraverse(aCallback, aField.Value(), aName, aFlags);
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
class NonNull {
|
|
public:
|
|
NonNull()
|
|
#ifdef DEBUG
|
|
: inited(false)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
// This is no worse than get() in terms of const handling.
|
|
operator T&() const {
|
|
MOZ_ASSERT(inited);
|
|
MOZ_ASSERT(ptr, "NonNull<T> was set to null");
|
|
return *ptr;
|
|
}
|
|
|
|
operator T*() const {
|
|
MOZ_ASSERT(inited);
|
|
MOZ_ASSERT(ptr, "NonNull<T> was set to null");
|
|
return ptr;
|
|
}
|
|
|
|
void operator=(T* t) {
|
|
ptr = t;
|
|
MOZ_ASSERT(ptr);
|
|
#ifdef DEBUG
|
|
inited = true;
|
|
#endif
|
|
}
|
|
|
|
template <typename U>
|
|
void operator=(U* t) {
|
|
ptr = t->ToAStringPtr();
|
|
MOZ_ASSERT(ptr);
|
|
#ifdef DEBUG
|
|
inited = true;
|
|
#endif
|
|
}
|
|
|
|
T** Slot() {
|
|
#ifdef DEBUG
|
|
inited = true;
|
|
#endif
|
|
return &ptr;
|
|
}
|
|
|
|
T* Ptr() {
|
|
MOZ_ASSERT(inited);
|
|
MOZ_ASSERT(ptr, "NonNull<T> was set to null");
|
|
return ptr;
|
|
}
|
|
|
|
// Make us work with smart-ptr helpers that expect a get()
|
|
T* get() const {
|
|
MOZ_ASSERT(inited);
|
|
MOZ_ASSERT(ptr);
|
|
return ptr;
|
|
}
|
|
|
|
protected:
|
|
// ptr is left uninitialized for optimization purposes.
|
|
MOZ_INIT_OUTSIDE_CTOR T* ptr;
|
|
#ifdef DEBUG
|
|
bool inited;
|
|
#endif
|
|
};
|
|
|
|
// Class for representing sequences in arguments. We use a non-auto array
|
|
// because that allows us to use sequences of sequences and the like. This
|
|
// needs to be fallible because web content controls the length of the array,
|
|
// and can easily try to create very large lengths.
|
|
template <typename T>
|
|
class Sequence : public FallibleTArray<T> {
|
|
public:
|
|
Sequence() : FallibleTArray<T>() {}
|
|
MOZ_IMPLICIT Sequence(FallibleTArray<T>&& aArray)
|
|
: FallibleTArray<T>(std::move(aArray)) {}
|
|
MOZ_IMPLICIT Sequence(nsTArray<T>&& aArray)
|
|
: FallibleTArray<T>(std::move(aArray)) {}
|
|
|
|
Sequence(Sequence&&) = default;
|
|
Sequence& operator=(Sequence&&) = default;
|
|
|
|
// XXX(Bug 1631461) Codegen.py must be adapted to allow making Sequence
|
|
// uncopyable.
|
|
Sequence(const Sequence& aOther) {
|
|
if (!this->AppendElements(aOther, fallible)) {
|
|
MOZ_CRASH("Out of memory");
|
|
}
|
|
}
|
|
Sequence& operator=(const Sequence& aOther) {
|
|
if (this != &aOther) {
|
|
this->Clear();
|
|
if (!this->AppendElements(aOther, fallible)) {
|
|
MOZ_CRASH("Out of memory");
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
inline nsWrapperCache* GetWrapperCache(nsWrapperCache* cache) { return cache; }
|
|
|
|
inline nsWrapperCache* GetWrapperCache(void* p) { return nullptr; }
|
|
|
|
// Helper template for smart pointers to resolve ambiguity between
|
|
// GetWrappeCache(void*) and GetWrapperCache(const ParentObject&).
|
|
template <template <typename> class SmartPtr, typename T>
|
|
inline nsWrapperCache* GetWrapperCache(const SmartPtr<T>& aObject) {
|
|
return GetWrapperCache(aObject.get());
|
|
}
|
|
|
|
enum class ReflectionScope { Content, NAC, UAWidget };
|
|
|
|
struct MOZ_STACK_CLASS ParentObject {
|
|
template <class T>
|
|
MOZ_IMPLICIT ParentObject(T* aObject)
|
|
: mObject(ToSupports(aObject)),
|
|
mWrapperCache(GetWrapperCache(aObject)),
|
|
mReflectionScope(ReflectionScope::Content) {}
|
|
|
|
template <class T, template <typename> class SmartPtr>
|
|
MOZ_IMPLICIT ParentObject(const SmartPtr<T>& aObject)
|
|
: mObject(aObject.get()),
|
|
mWrapperCache(GetWrapperCache(aObject.get())),
|
|
mReflectionScope(ReflectionScope::Content) {}
|
|
|
|
ParentObject(nsISupports* aObject, nsWrapperCache* aCache)
|
|
: mObject(aObject),
|
|
mWrapperCache(aCache),
|
|
mReflectionScope(ReflectionScope::Content) {}
|
|
|
|
// We don't want to make this an nsCOMPtr because of performance reasons, but
|
|
// it's safe because ParentObject is a stack class.
|
|
nsISupports* const MOZ_NON_OWNING_REF mObject;
|
|
nsWrapperCache* const mWrapperCache;
|
|
ReflectionScope mReflectionScope;
|
|
};
|
|
|
|
namespace binding_detail {
|
|
|
|
// Class for simple sequence arguments, only used internally by codegen.
|
|
template <typename T>
|
|
class AutoSequence : public AutoTArray<T, 16> {
|
|
public:
|
|
AutoSequence() : AutoTArray<T, 16>() {}
|
|
|
|
// Allow converting to const sequences as needed
|
|
operator const Sequence<T>&() const {
|
|
return *reinterpret_cast<const Sequence<T>*>(this);
|
|
}
|
|
};
|
|
|
|
} // namespace binding_detail
|
|
|
|
// Enum to represent a system or non-system caller type.
|
|
enum class CallerType : uint32_t { System, NonSystem };
|
|
|
|
// A class that can be passed (by value or const reference) to indicate that the
|
|
// caller is always a system caller. This can be used as the type of an
|
|
// argument to force only system callers to call a function.
|
|
class SystemCallerGuarantee {
|
|
public:
|
|
operator CallerType() const { return CallerType::System; }
|
|
};
|
|
|
|
class ProtoAndIfaceCache;
|
|
typedef void (*CreateInterfaceObjectsMethod)(JSContext* aCx,
|
|
JS::Handle<JSObject*> aGlobal,
|
|
ProtoAndIfaceCache& aCache,
|
|
bool aDefineOnGlobal);
|
|
JS::Handle<JSObject*> GetPerInterfaceObjectHandle(
|
|
JSContext* aCx, size_t aSlotId, CreateInterfaceObjectsMethod aCreator,
|
|
bool aDefineOnGlobal);
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|
|
|
|
#endif // mozilla_dom_BindingDeclarations_h__
|