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