/* -*- 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 nsQueryFrame_h #define nsQueryFrame_h #include #include "nscore.h" #include "mozilla/Assertions.h" // NOTE: the long lines in this file are intentional to make compiler error // messages more readable. #define NS_DECL_QUERYFRAME_TARGET(classname) \ static const nsQueryFrame::FrameIID kFrameIID = \ nsQueryFrame::classname##_id; \ typedef classname Has_NS_DECL_QUERYFRAME_TARGET; #define NS_DECL_QUERYFRAME void* QueryFrame(FrameIID id) const override; #define NS_QUERYFRAME_HEAD(class) \ void* class ::QueryFrame(FrameIID id) const { \ switch (id) { #define NS_QUERYFRAME_ENTRY(class) \ case class ::kFrameIID: { \ static_assert( \ std::is_same_v, \ #class " must declare itself as a queryframe target"); \ return const_cast(static_cast(this)); \ } #define NS_QUERYFRAME_ENTRY_CONDITIONAL(class, condition) \ case class ::kFrameIID: \ if (condition) { \ static_assert( \ std::is_same_v, \ #class " must declare itself as a queryframe target"); \ return const_cast(static_cast(this)); \ } \ break; #define NS_QUERYFRAME_TAIL_INHERITING(class) \ default: \ break; \ } \ return class ::QueryFrame(id); \ } #define NS_QUERYFRAME_TAIL_INHERITANCE_ROOT \ default: \ break; \ } \ MOZ_ASSERT(id != GetFrameId(), \ "A frame failed to QueryFrame to its *own type*. " \ "It may be missing NS_DECL_QUERYFRAME, or a " \ "NS_QUERYFRAME_ENTRY() line with its own type name"); \ return nullptr; \ } class nsQueryFrame { public: enum FrameIID { #define FRAME_ID(classname, ...) classname##_id, #define ABSTRACT_FRAME_ID(classname) classname##_id, #include "mozilla/FrameIdList.h" #undef FRAME_ID #undef ABSTRACT_FRAME_ID }; // A strict subset of FrameIID above for frame classes that we instantiate. enum class ClassID : uint8_t { #define FRAME_ID(classname, ...) classname##_id, #define ABSTRACT_FRAME_ID(classname) #include "mozilla/FrameIdList.h" #undef FRAME_ID #undef ABSTRACT_FRAME_ID }; virtual void* QueryFrame(FrameIID id) const = 0; }; class nsIFrame; template class do_QueryFrameHelper { public: explicit do_QueryFrameHelper(Source* s) : mRawPtr(s) {} // The return and argument types here are arbitrarily selected so no // corresponding member function exists. typedef void (do_QueryFrameHelper::*MatchNullptr)(double, float); // Implicit constructor for nullptr, trick borrowed from already_AddRefed. MOZ_IMPLICIT do_QueryFrameHelper(MatchNullptr aRawPtr) : mRawPtr(nullptr) {} template operator Dest*() { static_assert(std::is_same_v, typename Dest::Has_NS_DECL_QUERYFRAME_TARGET>, "Dest must declare itself as a queryframe target"); if (!mRawPtr) { return nullptr; } if (Dest* f = FastQueryFrame::QueryFrame(mRawPtr)) { MOZ_ASSERT( f == reinterpret_cast(mRawPtr->QueryFrame(Dest::kFrameIID)), "fast and slow paths should give the same result"); return f; } return reinterpret_cast(mRawPtr->QueryFrame(Dest::kFrameIID)); } private: // For non-nsIFrame types there is no fast-path. template struct FastQueryFrame { static Dst* QueryFrame(Src* aFrame) { return nullptr; } }; // Specialization for any nsIFrame type to any nsIFrame type -- if the source // instance's mClass matches kFrameIID of the destination type then // downcasting is safe. template struct FastQueryFrame< Src, Dst, std::enable_if_t::value>, std::enable_if_t::value>> { static Dst* QueryFrame(Src* aFrame) { return nsQueryFrame::FrameIID(aFrame->mClass) == Dst::kFrameIID ? reinterpret_cast(aFrame) : nullptr; } }; Source* mRawPtr; }; template inline do_QueryFrameHelper do_QueryFrame(T* s) { return do_QueryFrameHelper(s); } #endif // nsQueryFrame_h