summaryrefslogtreecommitdiffstats
path: root/layout/generic/nsQueryFrame.h
blob: 326b29266c964d8945e3f772ea8d87f31e82bb34 (plain)
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
/* -*- 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"

// NOTE: the long lines in this file are intentional to make compiler error
// messages more readable.

#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;

template <class 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.
  typedef void (do_QueryFrameHelper::*MatchNullptr)(double, float);
  // Implicit constructor for nullptr, trick borrowed from already_AddRefed.
  MOZ_IMPLICIT do_QueryFrameHelper(MatchNullptr aRawPtr) : mRawPtr(nullptr) {}

  template <class 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 (Dest* f = FastQueryFrame<Source, Dest>::QueryFrame(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:
  // For non-nsIFrame types there is no fast-path.
  template <class Src, class Dst, typename = void, typename = void>
  struct FastQueryFrame {
    static Dst* QueryFrame(Src* aFrame) { return nullptr; }
  };

  // Specialization for any nsIFrame type to any nsIFrame type -- if the source
  // instance's mClass matches kFrameIID of the destination type then
  // downcasting is safe.
  template <class Src, class Dst>
  struct FastQueryFrame<
      Src, Dst, std::enable_if_t<std::is_base_of<nsIFrame, Src>::value>,
      std::enable_if_t<std::is_base_of<nsIFrame, Dst>::value>> {
    static Dst* QueryFrame(Src* aFrame) {
      return nsQueryFrame::FrameIID(aFrame->mClass) == Dst::kFrameIID
                 ? reinterpret_cast<Dst*>(aFrame)
                 : nullptr;
    }
  };

  Source* mRawPtr;
};

template <class T>
inline do_QueryFrameHelper<T> do_QueryFrame(T* s) {
  return do_QueryFrameHelper<T>(s);
}

#endif  // nsQueryFrame_h