/* -*- 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 #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 aVal); bool StringifyToJSON(JSContext* aCx, JS::Handle 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 inline std::enable_if_t::value, void> ImplCycleCollectionUnlink(T& aDictionary) { aDictionary.UnlinkForCC(); } template inline std::enable_if_t::value, void> ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, T& aDictionary, const char* aName, uint32_t aFlags = 0) { aDictionary.TraverseForCC(aCallback, aFlags); } template inline std::enable_if_t::value, void> ImplCycleCollectionUnlink(UniquePtr& aDictionary) { aDictionary.reset(); } template inline std::enable_if_t::value, void> ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, UniquePtr& 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 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 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& aOther) const { return mImpl == aOther.mImpl; } bool operator!=(const Optional_base& aOther) const { return mImpl != aOther.mImpl; } template 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 InternalType& Construct(Args&&... aArgs) { mImpl.emplace(std::forward(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 mImpl; }; template class Optional : public Optional_base { public: MOZ_ALLOW_TEMPORARY Optional() : Optional_base() {} explicit Optional(const T& aValue) : Optional_base(aValue) {} Optional(Optional&&) = default; }; template class Optional> : public Optional_base, JS::Rooted> { public: MOZ_ALLOW_TEMPORARY Optional() : Optional_base, JS::Rooted>() {} explicit Optional(JSContext* cx) : Optional_base, JS::Rooted>() { this->Construct(cx); } Optional(JSContext* cx, const T& aValue) : Optional_base, JS::Rooted>(cx, aValue) {} // Override the const Value() to return the right thing so we're not // returning references to temporaries. JS::Handle 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& 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 : public Optional_base { public: Optional() = default; explicit Optional(JSObject* aValue) : Optional_base(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::Construct( static_cast(nullptr)); } template JSObject*& Construct(const T1& t1) { return Optional_base::Construct(t1); } }; // A specialization of Optional for JS::Value to make sure no one ever uses it. template <> class Optional { 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 class NonNull; template class Optional> : public Optional_base> { 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& Value() { return *this->mImpl; } }; // A specialization of Optional for OwningNonNull that lets us get a // T& from Value() template class Optional> : public Optional_base> { 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& 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 struct FakeString; } // namespace binding_detail template class Optional> { using AString = nsTSubstring; 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* str) { MOZ_ASSERT(str); mStr = reinterpret_cast*>(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 inline void ImplCycleCollectionUnlink(Optional& aField) { if (aField.WasPassed()) { ImplCycleCollectionUnlink(aField.Value()); } } template inline void ImplCycleCollectionTraverse( nsCycleCollectionTraversalCallback& aCallback, Optional& aField, const char* aName, uint32_t aFlags = 0) { if (aField.WasPassed()) { ImplCycleCollectionTraverse(aCallback, aField.Value(), aName, aFlags); } } template 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 was set to null"); return *ptr; } operator T*() const { MOZ_ASSERT(inited); MOZ_ASSERT(ptr, "NonNull was set to null"); return ptr; } void operator=(T* t) { ptr = t; MOZ_ASSERT(ptr); #ifdef DEBUG inited = true; #endif } template 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 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 class Sequence : public FallibleTArray { public: Sequence() : FallibleTArray() {} MOZ_IMPLICIT Sequence(FallibleTArray&& aArray) : FallibleTArray(std::move(aArray)) {} MOZ_IMPLICIT Sequence(nsTArray&& aArray) : FallibleTArray(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