nsQueryFrame.h (6783B)
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 "mozilla/Assertions.h" 13 #include "nscore.h" 14 15 namespace mozilla { 16 class ScrollContainerFrame; 17 } 18 19 #define NS_DECL_QUERYFRAME_TARGET(classname) \ 20 static const nsQueryFrame::FrameIID kFrameIID = \ 21 nsQueryFrame::classname##_id; \ 22 typedef classname Has_NS_DECL_QUERYFRAME_TARGET; 23 24 #define NS_DECL_QUERYFRAME void* QueryFrame(FrameIID id) const override; 25 26 #define NS_QUERYFRAME_HEAD(class) \ 27 void* class ::QueryFrame(FrameIID id) const { \ 28 switch (id) { 29 #define NS_QUERYFRAME_ENTRY(class) \ 30 case class ::kFrameIID: { \ 31 static_assert( \ 32 std::is_same_v<class, class ::Has_NS_DECL_QUERYFRAME_TARGET>, \ 33 #class " must declare itself as a queryframe target"); \ 34 return const_cast<class*>(static_cast<const class*>(this)); \ 35 } 36 37 #define NS_QUERYFRAME_ENTRY_CONDITIONAL(class, condition) \ 38 case class ::kFrameIID: \ 39 if (condition) { \ 40 static_assert( \ 41 std::is_same_v<class, class ::Has_NS_DECL_QUERYFRAME_TARGET>, \ 42 #class " must declare itself as a queryframe target"); \ 43 return const_cast<class*>(static_cast<const class*>(this)); \ 44 } \ 45 break; 46 47 #define NS_QUERYFRAME_TAIL_INHERITING(class) \ 48 default: \ 49 break; \ 50 } \ 51 return class ::QueryFrame(id); \ 52 } 53 54 #define NS_QUERYFRAME_TAIL_INHERITANCE_ROOT \ 55 default: \ 56 break; \ 57 } \ 58 MOZ_ASSERT(id != GetFrameId(), \ 59 "A frame failed to QueryFrame to its *own type*. " \ 60 "It may be missing NS_DECL_QUERYFRAME, or a " \ 61 "NS_QUERYFRAME_ENTRY() line with its own type name"); \ 62 return nullptr; \ 63 } 64 65 class nsQueryFrame { 66 public: 67 enum FrameIID { 68 #define FRAME_ID(classname, ...) classname##_id, 69 #define ABSTRACT_FRAME_ID(classname) classname##_id, 70 #include "mozilla/FrameIdList.h" 71 #undef FRAME_ID 72 #undef ABSTRACT_FRAME_ID 73 }; 74 75 // A strict subset of FrameIID above for frame classes that we instantiate. 76 enum class ClassID : uint8_t { 77 #define FRAME_ID(classname, ...) classname##_id, 78 #define ABSTRACT_FRAME_ID(classname) 79 #include "mozilla/FrameIdList.h" 80 #undef FRAME_ID 81 #undef ABSTRACT_FRAME_ID 82 }; 83 84 virtual void* QueryFrame(FrameIID id) const = 0; 85 }; 86 87 class nsIFrame; 88 89 namespace detail { 90 template <typename Dest, typename = void> 91 struct FastQueryFrame { 92 static constexpr bool kSupported = false; 93 }; 94 95 // For final classes we can check the class id. 96 template <typename Dest> 97 struct FastQueryFrame<Dest, std::enable_if_t<std::is_final_v<Dest>, void>> { 98 static constexpr bool kSupported = true; 99 100 template <typename Src> 101 static Dest* QueryFrame(Src* aPtr) { 102 return nsQueryFrame::FrameIID(aPtr->GetClassID()) == Dest::kFrameIID 103 ? static_cast<Dest*>(aPtr) 104 : nullptr; 105 } 106 }; 107 108 #define IMPL_FAST_QUERYFRAME(Dest_, Check_) \ 109 template <> \ 110 struct FastQueryFrame<Dest_, void> { \ 111 static constexpr bool kSupported = true; \ 112 template <typename Src> \ 113 static Dest_* QueryFrame(Src* aPtr) { \ 114 return aPtr->Check_() ? static_cast<Dest_*>(aPtr) : nullptr; \ 115 } \ 116 } 117 118 IMPL_FAST_QUERYFRAME(mozilla::ScrollContainerFrame, 119 IsScrollContainerOrSubclass); 120 121 #undef IMPL_FAST_QUERYFRAME 122 } 123 124 template <typename Source> 125 class do_QueryFrameHelper { 126 public: 127 explicit do_QueryFrameHelper(Source* s) : mRawPtr(s) {} 128 129 // The return and argument types here are arbitrarily selected so no 130 // corresponding member function exists. 131 using MatchNullptr = void (*)(double, float); 132 // Implicit constructor for nullptr, trick borrowed from already_AddRefed. 133 MOZ_IMPLICIT do_QueryFrameHelper(MatchNullptr aRawPtr) : mRawPtr(nullptr) {} 134 135 template <typename Dest> 136 operator Dest*() { 137 static_assert(std::is_same_v<std::remove_const_t<Dest>, 138 typename Dest::Has_NS_DECL_QUERYFRAME_TARGET>, 139 "Dest must declare itself as a queryframe target"); 140 if (!mRawPtr) { 141 return nullptr; 142 } 143 if constexpr (::detail::FastQueryFrame<Dest>::kSupported) { 144 static_assert( 145 std::is_base_of_v<nsIFrame, Source>, 146 "We only support fast do_QueryFrame() where the source must be a " 147 "derived class of nsIFrame. Consider a two-step do_QueryFrame() " 148 "(once to nsIFrame, another to the target) if absolutely needed."); 149 Dest* f = ::detail::FastQueryFrame<Dest>::QueryFrame(mRawPtr); 150 MOZ_ASSERT( 151 f == reinterpret_cast<Dest*>(mRawPtr->QueryFrame(Dest::kFrameIID)), 152 "fast and slow paths should give the same result"); 153 return f; 154 } 155 if constexpr (std::is_base_of_v<nsIFrame, Source> && 156 std::is_base_of_v<nsIFrame, Dest>) { 157 // For non-final frames we can still optimize the virtual call some of the 158 // time. 159 if (nsQueryFrame::FrameIID(mRawPtr->GetClassID()) == Dest::kFrameIID) { 160 auto* f = static_cast<Dest*>(mRawPtr); 161 MOZ_ASSERT( 162 f == reinterpret_cast<Dest*>(mRawPtr->QueryFrame(Dest::kFrameIID)), 163 "fast and slow paths should give the same result"); 164 return f; 165 } 166 } 167 return reinterpret_cast<Dest*>(mRawPtr->QueryFrame(Dest::kFrameIID)); 168 } 169 170 private: 171 Source* mRawPtr; 172 }; 173 174 template <typename T> 175 inline do_QueryFrameHelper<T> do_QueryFrame(T* s) { 176 return do_QueryFrameHelper<T>(s); 177 } 178 179 #endif // nsQueryFrame_h