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