1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef nsQueryFrame_h 8 #define nsQueryFrame_h 9 10 #include <type_traits> 11 12 #include "nscore.h" 13 #include "mozilla/Assertions.h" 14 15 // NOTE: the long lines in this file are intentional to make compiler error 16 // messages more readable. 17 18 #define NS_DECL_QUERYFRAME_TARGET(classname) \ 19 static const nsQueryFrame::FrameIID kFrameIID = \ 20 nsQueryFrame::classname##_id; \ 21 typedef classname Has_NS_DECL_QUERYFRAME_TARGET; 22 23 #define NS_DECL_QUERYFRAME void* QueryFrame(FrameIID id) const override; 24 25 #define NS_QUERYFRAME_HEAD(class) \ 26 void* class ::QueryFrame(FrameIID id) const { \ 27 switch (id) { 28 #define NS_QUERYFRAME_ENTRY(class) \ 29 case class ::kFrameIID: { \ 30 static_assert( \ 31 std::is_same_v<class, class ::Has_NS_DECL_QUERYFRAME_TARGET>, \ 32 #class " must declare itself as a queryframe target"); \ 33 return const_cast<class*>(static_cast<const class*>(this)); \ 34 } 35 36 #define NS_QUERYFRAME_ENTRY_CONDITIONAL(class, condition) \ 37 case class ::kFrameIID: \ 38 if (condition) { \ 39 static_assert( \ 40 std::is_same_v<class, class ::Has_NS_DECL_QUERYFRAME_TARGET>, \ 41 #class " must declare itself as a queryframe target"); \ 42 return const_cast<class*>(static_cast<const class*>(this)); \ 43 } \ 44 break; 45 46 #define NS_QUERYFRAME_TAIL_INHERITING(class) \ 47 default: \ 48 break; \ 49 } \ 50 return class ::QueryFrame(id); \ 51 } 52 53 #define NS_QUERYFRAME_TAIL_INHERITANCE_ROOT \ 54 default: \ 55 break; \ 56 } \ 57 MOZ_ASSERT(id != GetFrameId(), \ 58 "A frame failed to QueryFrame to its *own type*. " \ 59 "It may be missing NS_DECL_QUERYFRAME, or a " \ 60 "NS_QUERYFRAME_ENTRY() line with its own type name"); \ 61 return nullptr; \ 62 } 63 64 class nsQueryFrame { 65 public: 66 enum FrameIID { 67 #define FRAME_ID(classname, ...) classname##_id, 68 #define ABSTRACT_FRAME_ID(classname) classname##_id, 69 #include "mozilla/FrameIdList.h" 70 #undef FRAME_ID 71 #undef ABSTRACT_FRAME_ID 72 }; 73 74 // A strict subset of FrameIID above for frame classes that we instantiate. 75 enum class ClassID : uint8_t { 76 #define FRAME_ID(classname, ...) classname##_id, 77 #define ABSTRACT_FRAME_ID(classname) 78 #include "mozilla/FrameIdList.h" 79 #undef FRAME_ID 80 #undef ABSTRACT_FRAME_ID 81 }; 82 83 virtual void* QueryFrame(FrameIID id) const = 0; 84 }; 85 86 class nsIFrame; 87 88 template <class Source> 89 class do_QueryFrameHelper { 90 public: do_QueryFrameHelper(Source * s)91 explicit do_QueryFrameHelper(Source* s) : mRawPtr(s) {} 92 93 // The return and argument types here are arbitrarily selected so no 94 // corresponding member function exists. 95 typedef void (do_QueryFrameHelper::*MatchNullptr)(double, float); 96 // Implicit constructor for nullptr, trick borrowed from already_AddRefed. do_QueryFrameHelper(MatchNullptr aRawPtr)97 MOZ_IMPLICIT do_QueryFrameHelper(MatchNullptr aRawPtr) : mRawPtr(nullptr) {} 98 99 template <class Dest> 100 operator Dest*() { 101 static_assert(std::is_same_v<std::remove_const_t<Dest>, 102 typename Dest::Has_NS_DECL_QUERYFRAME_TARGET>, 103 "Dest must declare itself as a queryframe target"); 104 if (!mRawPtr) { 105 return nullptr; 106 } 107 if (Dest* f = FastQueryFrame<Source, Dest>::QueryFrame(mRawPtr)) { 108 MOZ_ASSERT( 109 f == reinterpret_cast<Dest*>(mRawPtr->QueryFrame(Dest::kFrameIID)), 110 "fast and slow paths should give the same result"); 111 return f; 112 } 113 return reinterpret_cast<Dest*>(mRawPtr->QueryFrame(Dest::kFrameIID)); 114 } 115 116 private: 117 // For non-nsIFrame types there is no fast-path. 118 template <class Src, class Dst, typename = void, typename = void> 119 struct FastQueryFrame { QueryFrameFastQueryFrame120 static Dst* QueryFrame(Src* aFrame) { return nullptr; } 121 }; 122 123 // Specialization for any nsIFrame type to any nsIFrame type -- if the source 124 // instance's mClass matches kFrameIID of the destination type then 125 // downcasting is safe. 126 template <class Src, class Dst> 127 struct FastQueryFrame< 128 Src, Dst, std::enable_if_t<std::is_base_of<nsIFrame, Src>::value>, 129 std::enable_if_t<std::is_base_of<nsIFrame, Dst>::value>> { 130 static Dst* QueryFrame(Src* aFrame) { 131 return nsQueryFrame::FrameIID(aFrame->mClass) == Dst::kFrameIID 132 ? reinterpret_cast<Dst*>(aFrame) 133 : nullptr; 134 } 135 }; 136 137 Source* mRawPtr; 138 }; 139 140 template <class T> 141 inline do_QueryFrameHelper<T> do_QueryFrame(T* s) { 142 return do_QueryFrameHelper<T>(s); 143 } 144 145 #endif // nsQueryFrame_h 146