diff options
Diffstat (limited to 'dom/bindings/BindingDeclarations.h')
-rw-r--r-- | dom/bindings/BindingDeclarations.h | 547 |
1 files changed, 547 insertions, 0 deletions
diff --git a/dom/bindings/BindingDeclarations.h b/dom/bindings/BindingDeclarations.h new file mode 100644 index 0000000000..8209f6323d --- /dev/null +++ b/dom/bindings/BindingDeclarations.h @@ -0,0 +1,547 @@ +/* -*- 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__ |