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 "nscore.h" 11 #include "mozilla/Assertions.h" 12 #include "mozilla/TypeTraits.h" 13 14 // NOTE: the long lines in this file are intentional to make compiler error 15 // messages more readable. 16 17 #define NS_DECL_QUERYFRAME_TARGET(classname) \ 18 static const nsQueryFrame::FrameIID kFrameIID = \ 19 nsQueryFrame::classname##_id; \ 20 typedef classname Has_NS_DECL_QUERYFRAME_TARGET; 21 22 #define NS_DECL_QUERYFRAME void* QueryFrame(FrameIID id) override; 23 24 #define NS_QUERYFRAME_HEAD(class) \ 25 void* class ::QueryFrame(FrameIID id) { \ 26 switch (id) { 27 #define NS_QUERYFRAME_ENTRY(class) \ 28 case class ::kFrameIID: { \ 29 static_assert( \ 30 mozilla::IsSame<class, class ::Has_NS_DECL_QUERYFRAME_TARGET>::value, \ 31 #class " must declare itself as a queryframe target"); \ 32 return static_cast<class*>(this); \ 33 } 34 35 #define NS_QUERYFRAME_ENTRY_CONDITIONAL(class, condition) \ 36 case class ::kFrameIID: \ 37 if (condition) { \ 38 static_assert( \ 39 mozilla::IsSame<class, \ 40 class ::Has_NS_DECL_QUERYFRAME_TARGET>::value, \ 41 #class " must declare itself as a queryframe target"); \ 42 return static_cast<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 "nsFrameIdList.h" 70 #undef FRAME_ID 71 #undef ABSTRACT_FRAME_ID 72 73 // This marker allows mozilla::ArenaObjectID to "extend" this enum 74 // with additional sequential values for use in nsPresArena and 75 // nsIPresShell::{Allocate,Free}ByObjectId 76 NON_FRAME_MARKER 77 }; 78 79 // A strict subset of FrameIID above for frame classes that we instantiate. 80 enum class ClassID : uint8_t { 81 #define FRAME_ID(classname, ...) classname##_id, 82 #define ABSTRACT_FRAME_ID(classname) 83 #include "nsFrameIdList.h" 84 #undef FRAME_ID 85 #undef ABSTRACT_FRAME_ID 86 }; 87 88 virtual void* QueryFrame(FrameIID id) = 0; 89 }; 90 91 class nsIFrame; 92 93 template <class Source> 94 class do_QueryFrameHelper { 95 public: do_QueryFrameHelper(Source * s)96 explicit do_QueryFrameHelper(Source* s) : mRawPtr(s) {} 97 98 // The return and argument types here are arbitrarily selected so no 99 // corresponding member function exists. 100 typedef void (do_QueryFrameHelper::*MatchNullptr)(double, float); 101 // Implicit constructor for nullptr, trick borrowed from already_AddRefed. do_QueryFrameHelper(MatchNullptr aRawPtr)102 MOZ_IMPLICIT do_QueryFrameHelper(MatchNullptr aRawPtr) : mRawPtr(nullptr) {} 103 104 template <class Dest> 105 operator Dest*() { 106 static_assert( 107 mozilla::IsSame<Dest, 108 typename Dest::Has_NS_DECL_QUERYFRAME_TARGET>::value, 109 "Dest must declare itself as a queryframe target"); 110 if (!mRawPtr) { 111 return nullptr; 112 } 113 if (Dest* f = FastQueryFrame<Source, Dest>::QueryFrame(mRawPtr)) { 114 MOZ_ASSERT( 115 f == reinterpret_cast<Dest*>(mRawPtr->QueryFrame(Dest::kFrameIID)), 116 "fast and slow paths should give the same result"); 117 return f; 118 } 119 return reinterpret_cast<Dest*>(mRawPtr->QueryFrame(Dest::kFrameIID)); 120 } 121 122 private: 123 // For non-nsIFrame types there is no fast-path. 124 template <class Src, class Dst, typename = void, typename = void> 125 struct FastQueryFrame { QueryFrameFastQueryFrame126 static Dst* QueryFrame(Src* aFrame) { return nullptr; } 127 }; 128 129 // Specialization for any nsIFrame type to any nsIFrame type -- if the source 130 // instance's mClass matches kFrameIID of the destination type then 131 // downcasting is safe. 132 template <class Src, class Dst> 133 struct FastQueryFrame< 134 Src, Dst, 135 typename mozilla::EnableIf<mozilla::IsBaseOf<nsIFrame, Src>::value>::Type, 136 typename mozilla::EnableIf< 137 mozilla::IsBaseOf<nsIFrame, Dst>::value>::Type> { 138 static Dst* QueryFrame(Src* aFrame) { 139 return nsQueryFrame::FrameIID(aFrame->mClass) == Dst::kFrameIID 140 ? reinterpret_cast<Dst*>(aFrame) 141 : nullptr; 142 } 143 }; 144 145 Source* mRawPtr; 146 }; 147 148 template <class T> 149 inline do_QueryFrameHelper<T> do_QueryFrame(T* s) { 150 return do_QueryFrameHelper<T>(s); 151 } 152 153 #endif // nsQueryFrame_h 154