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