tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DOMJSClass.h (25667B)


      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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_dom_DOMJSClass_h
      8 #define mozilla_dom_DOMJSClass_h
      9 
     10 #include "js/Object.h"  // JS::GetClass, JS::GetReservedSlot
     11 #include "js/Wrapper.h"
     12 #include "jsapi.h"
     13 #include "jsfriendapi.h"
     14 #include "mozilla/Assertions.h"
     15 #include "mozilla/Attributes.h"
     16 #include "mozilla/Likely.h"
     17 #include "mozilla/OriginTrials.h"
     18 #include "mozilla/dom/PrototypeList.h"  // auto-generated
     19 #include "mozilla/dom/WebIDLPrefs.h"    // auto-generated
     20 
     21 class nsCycleCollectionParticipant;
     22 class nsWrapperCache;
     23 struct JSFunctionSpec;
     24 struct JSPropertySpec;
     25 struct JSStructuredCloneReader;
     26 struct JSStructuredCloneWriter;
     27 class nsIGlobalObject;
     28 
     29 // All DOM globals must have a slot at DOM_PROTOTYPE_SLOT.
     30 #define DOM_PROTOTYPE_SLOT JSCLASS_GLOBAL_SLOT_COUNT
     31 
     32 // Keep this count up to date with any extra global slots added above.
     33 #define DOM_GLOBAL_SLOTS 1
     34 
     35 // We use these flag bits for the new bindings.
     36 #define JSCLASS_DOM_GLOBAL JSCLASS_USERBIT1
     37 #define JSCLASS_IS_DOMIFACEANDPROTOJSCLASS JSCLASS_USERBIT2
     38 
     39 namespace mozilla::dom {
     40 
     41 /**
     42 * Returns true if code running in the given JSContext is allowed to access
     43 * [SecureContext] API on the given JSObject.
     44 *
     45 * [SecureContext] API exposure is restricted to use by code in a Secure
     46 * Contexts:
     47 *
     48 *   https://w3c.github.io/webappsec-secure-contexts/
     49 *
     50 * Since we want [SecureContext] exposure to depend on the privileges of the
     51 * running code (rather than the privileges of an object's creator), this
     52 * function checks to see whether the given JSContext's Realm is flagged
     53 * as a Secure Context.  That allows us to make sure that system principal code
     54 * (which is marked as a Secure Context) can access Secure Context API on an
     55 * object in a different realm, regardless of whether the other realm is a
     56 * Secure Context or not.
     57 *
     58 * Checking the JSContext's Realm doesn't work for expanded principal
     59 * globals accessing a Secure Context web page though (e.g. those used by frame
     60 * scripts).  To handle that we fall back to checking whether the JSObject came
     61 * from a Secure Context.
     62 *
     63 * Note: We'd prefer this function to live in BindingUtils.h, but we need to
     64 * call it in this header, and BindingUtils.h includes us (i.e. we'd have a
     65 * circular dependency between headers if it lived there).
     66 */
     67 inline bool IsSecureContextOrObjectIsFromSecureContext(JSContext* aCx,
     68                                                       JSObject* aObj) {
     69  MOZ_ASSERT(!js::IsWrapper(aObj));
     70  return JS::GetIsSecureContext(js::GetContextRealm(aCx)) ||
     71         JS::GetIsSecureContext(js::GetNonCCWObjectRealm(aObj));
     72 }
     73 
     74 typedef bool (*ResolveOwnProperty)(
     75    JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj,
     76    JS::Handle<jsid> id,
     77    JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
     78 
     79 typedef bool (*EnumerateOwnProperties)(JSContext* cx,
     80                                       JS::Handle<JSObject*> wrapper,
     81                                       JS::Handle<JSObject*> obj,
     82                                       JS::MutableHandleVector<jsid> props);
     83 
     84 typedef bool (*DeleteNamedProperty)(JSContext* cx,
     85                                    JS::Handle<JSObject*> wrapper,
     86                                    JS::Handle<JSObject*> obj,
     87                                    JS::Handle<jsid> id,
     88                                    JS::ObjectOpResult& opresult);
     89 
     90 // Returns true if the given global is of a type whose bit is set in
     91 // aGlobalSet.
     92 bool IsGlobalInExposureSet(JSContext* aCx, JSObject* aGlobal,
     93                           uint32_t aGlobalSet);
     94 
     95 struct ConstantSpec {
     96  const char* name;
     97  JS::Value value;
     98 };
     99 
    100 typedef bool (*PropertyEnabled)(JSContext* cx, JSObject* global);
    101 
    102 namespace GlobalNames {
    103 // The names of our possible globals.  These are the names of the actual
    104 // interfaces, not of the global names used to refer to them in IDL [Exposed]
    105 // annotations.
    106 static const uint32_t Window = 1u << 0;
    107 static const uint32_t DedicatedWorkerGlobalScope = 1u << 1;
    108 static const uint32_t SharedWorkerGlobalScope = 1u << 2;
    109 static const uint32_t ServiceWorkerGlobalScope = 1u << 3;
    110 static const uint32_t WorkerDebuggerGlobalScope = 1u << 4;
    111 static const uint32_t AudioWorkletGlobalScope = 1u << 5;
    112 static const uint32_t PaintWorkletGlobalScope = 1u << 6;
    113 static const uint32_t ShadowRealmGlobalScope = 1u << 7;
    114 
    115 static constexpr uint32_t kCount = 8;
    116 }  // namespace GlobalNames
    117 
    118 struct PrefableDisablers {
    119  inline bool isEnabled(JSContext* cx, JS::Handle<JSObject*> obj) const {
    120    if (nonExposedGlobals &&
    121        IsGlobalInExposureSet(cx, JS::GetNonCCWObjectGlobal(obj),
    122                              nonExposedGlobals)) {
    123      return false;
    124    }
    125    if (prefIndex != WebIDLPrefIndex::NoPref &&
    126        !sWebIDLPrefs[uint16_t(prefIndex)]()) {
    127      return false;
    128    }
    129    if (secureContext && !IsSecureContextOrObjectIsFromSecureContext(cx, obj)) {
    130      return false;
    131    }
    132    if (trial != OriginTrial(0) &&
    133        !OriginTrials::IsEnabled(cx, JS::GetNonCCWObjectGlobal(obj), trial)) {
    134      // TODO(emilio): Perhaps reconsider the interaction between [Trial=""] and
    135      // [Pref=""].
    136      //
    137      // In particular, it might be desirable to only check the trial if there
    138      // is no pref or the pref is disabled.
    139      return false;
    140    }
    141    if (enabledFunc && !enabledFunc(cx, JS::GetNonCCWObjectGlobal(obj))) {
    142      return false;
    143    }
    144    return true;
    145  }
    146 
    147  // Index into the array of StaticPrefs
    148  const WebIDLPrefIndex prefIndex;
    149 
    150  // Bitmask of global names that we should not be exposed in.
    151  const uint16_t nonExposedGlobals : GlobalNames::kCount;
    152 
    153  // A boolean indicating whether a Secure Context is required.
    154  const uint16_t secureContext : 1;
    155 
    156  // An origin trial controlling the feature. This can be made a bitfield too if
    157  // needed.
    158  const OriginTrial trial;
    159 
    160  // A function pointer to a function that can say the property is disabled
    161  // even if "enabled" is set to true.  If the pointer is null the value of
    162  // "enabled" is used as-is.
    163  const PropertyEnabled enabledFunc;
    164 };
    165 
    166 template <typename T>
    167 struct Prefable {
    168  inline bool isEnabled(JSContext* cx, JS::Handle<JSObject*> obj) const {
    169    MOZ_ASSERT(!js::IsWrapper(obj));
    170    if (MOZ_LIKELY(!disablers)) {
    171      return true;
    172    }
    173    return disablers->isEnabled(cx, obj);
    174  }
    175 
    176  // Things that can disable this set of specs. |nullptr| means "cannot be
    177  // disabled".
    178  const PrefableDisablers* const disablers;
    179 
    180  // Array of specs, terminated in whatever way is customary for T.
    181  // Null to indicate a end-of-array for Prefable, when such an
    182  // indicator is needed.
    183  const T* const specs;
    184 };
    185 
    186 enum PropertyType {
    187  eStaticMethod,
    188  eStaticAttribute,
    189  eMethod,
    190  eAttribute,
    191  eUnforgeableMethod,
    192  eUnforgeableAttribute,
    193  eConstant,
    194  ePropertyTypeCount
    195 };
    196 
    197 #define NUM_BITS_PROPERTY_INFO_TYPE 3
    198 #define NUM_BITS_PROPERTY_INFO_PREF_INDEX 13
    199 #define NUM_BITS_PROPERTY_INFO_SPEC_INDEX 16
    200 
    201 struct PropertyInfo {
    202 private:
    203  // MSVC generates static initializers if we store a jsid here, even if
    204  // PropertyInfo has a constexpr constructor. See bug 1460341 and bug 1464036.
    205  uintptr_t mIdBits;
    206 
    207 public:
    208  // One of PropertyType, will be used for accessing the corresponding Duo in
    209  // NativePropertiesN.duos[].
    210  uint32_t type : NUM_BITS_PROPERTY_INFO_TYPE;
    211  // The index to the corresponding Preable in Duo.mPrefables[].
    212  uint32_t prefIndex : NUM_BITS_PROPERTY_INFO_PREF_INDEX;
    213  // The index to the corresponding spec in Duo.mPrefables[prefIndex].specs[].
    214  uint32_t specIndex : NUM_BITS_PROPERTY_INFO_SPEC_INDEX;
    215 
    216  void SetId(jsid aId) {
    217    static_assert(sizeof(jsid) == sizeof(mIdBits),
    218                  "jsid should fit in mIdBits");
    219    mIdBits = aId.asRawBits();
    220  }
    221  MOZ_ALWAYS_INLINE jsid Id() const { return jsid::fromRawBits(mIdBits); }
    222 
    223  bool IsStaticMethod() const { return type == eStaticMethod; }
    224 
    225  static int Compare(const PropertyInfo& aInfo1, const PropertyInfo& aInfo2) {
    226    // IdToIndexComparator needs to be updated if the order here is changed!
    227    if (MOZ_UNLIKELY(aInfo1.mIdBits == aInfo2.mIdBits)) {
    228      MOZ_ASSERT((aInfo1.type == eMethod || aInfo1.type == eStaticMethod) &&
    229                 (aInfo2.type == eMethod || aInfo2.type == eStaticMethod));
    230 
    231      bool isStatic1 = aInfo1.IsStaticMethod();
    232 
    233      MOZ_ASSERT(isStatic1 != aInfo2.IsStaticMethod(),
    234                 "We shouldn't have 2 static methods with the same name!");
    235 
    236      return isStatic1 ? -1 : 1;
    237    }
    238 
    239    return aInfo1.mIdBits < aInfo2.mIdBits ? -1 : 1;
    240  }
    241 };
    242 
    243 static_assert(
    244    ePropertyTypeCount <= 1ull << NUM_BITS_PROPERTY_INFO_TYPE,
    245    "We have property type count that is > (1 << NUM_BITS_PROPERTY_INFO_TYPE)");
    246 
    247 // Conceptually, NativeProperties has seven (Prefable<T>*, PropertyInfo*) duos
    248 // (where T is one of JSFunctionSpec, JSPropertySpec, or ConstantSpec), one for
    249 // each of: static methods and attributes, methods and attributes, unforgeable
    250 // methods and attributes, and constants.
    251 //
    252 // That's 14 pointers, but in most instances most of the duos are all null, and
    253 // there are many instances. To save space we use a variable-length type,
    254 // NativePropertiesN<N>, to hold the data and getters to access it. It has N
    255 // actual duos (stored in duos[]), plus four bits for each of the 7 possible
    256 // duos: 1 bit that states if that duo is present, and 3 that state that duo's
    257 // offset (if present) in duos[].
    258 //
    259 // All duo accesses should be done via the getters, which contain assertions
    260 // that check we don't overrun the end of the struct. (The duo data members are
    261 // public only so they can be statically initialized.) These assertions should
    262 // never fail so long as (a) accesses to the variable-length part are guarded by
    263 // appropriate Has*() calls, and (b) all instances are well-formed, i.e. the
    264 // value of N matches the number of mHas* members that are true.
    265 //
    266 // We store all the property ids a NativePropertiesN owns in a single array of
    267 // PropertyInfo structs. Each struct contains an id and the information needed
    268 // to find the corresponding Prefable for the enabled check, as well as the
    269 // information needed to find the correct property descriptor in the
    270 // Prefable. We also store an array of indices into the PropertyInfo array,
    271 // sorted by bits of the corresponding jsid. Given a jsid, this allows us to
    272 // binary search for the index of the corresponding PropertyInfo, if any.
    273 //
    274 // Finally, we define a typedef of NativePropertiesN<7>, NativeProperties, which
    275 // we use as a "base" type used to refer to all instances of NativePropertiesN.
    276 // (7 is used because that's the maximum valid parameter, though any other
    277 // value 1..6 could also be used.) This is reasonable because of the
    278 // aforementioned assertions in the getters. Upcast() is used to convert
    279 // specific instances to this "base" type.
    280 //
    281 // An example
    282 // ----------
    283 // NativeProperties points to various things, and it can be hard to keep track.
    284 // The following example shows the layout.
    285 //
    286 // Imagine an example interface, with:
    287 // - 10 properties
    288 //   - 6 methods, 3 with no disablers struct, 2 sharing the same disablers
    289 //     struct, 1 using a different disablers struct
    290 //   - 4 attributes, all with no disablers
    291 // - The property order is such that those using the same disablers structs are
    292 //   together. (This is not guaranteed, but it makes the example simpler.)
    293 //
    294 // Each PropertyInfo also contain indices into sMethods/sMethods_specs (for
    295 // method infos) and sAttributes/sAttributes_specs (for attributes), which let
    296 // them find their spec, but these are not shown.
    297 //
    298 //   sNativeProperties             sNativeProperties_        sNativeProperties_
    299 //   ----                          sortedPropertyIndices[10] propertyInfos[10]
    300 //   - <several scalar fields>     ----                      ----
    301 //   - sortedPropertyIndices ----> <10 indices>         +--> 0 info (method)
    302 //   - duos[2]                     ----                 |    1 info (method)
    303 //     ----(methods)                                    |    2 info (method)
    304 //     0 - mPrefables -------> points to sMethods below |    3 info (method)
    305 //       - mPropertyInfos ------------------------------+    4 info (method)
    306 //     1 - mPrefables -------> points to sAttributes below   5 info (method)
    307 //       - mPropertyInfos ---------------------------------> 6 info (attr)
    308 //     ----                                                  7 info (attr)
    309 //   ----                                                    8 info (attr)
    310 //                                                           9 info (attr)
    311 //                                                           ----
    312 //
    313 // sMethods has three entries (excluding the terminator) because there are
    314 // three disablers structs. The {nullptr,nullptr} serves as the terminator.
    315 // There are also END terminators within sMethod_specs; the need for these
    316 // terminators (as opposed to a length) is deeply embedded in SpiderMonkey.
    317 // Disablers structs are suffixed with the index of the first spec they cover.
    318 //
    319 //   sMethods                               sMethods_specs
    320 //   ----                                   ----
    321 //   0 - nullptr                     +----> 0 spec
    322 //     - specs ----------------------+      1 spec
    323 //   1 - disablers ---> disablers4          2 spec
    324 //     - specs ------------------------+    3 END
    325 //   2 - disablers ---> disablers7     +--> 4 spec
    326 //     - specs ----------------------+      5 spec
    327 //   3 - nullptr                     |      6 END
    328 //     - nullptr                     +----> 7 spec
    329 //   ----                                   8 END
    330 //
    331 // sAttributes has a single entry (excluding the terminator) because all of the
    332 // specs lack disablers.
    333 //
    334 //   sAttributes                            sAttributes_specs
    335 //   ----                                   ----
    336 //   0 - nullptr                     +----> 0 spec
    337 //     - specs ----------------------+      1 spec
    338 //   1 - nullptr                            2 spec
    339 //     - nullptr                            3 spec
    340 //   ----                                   4 END
    341 //                                          ----
    342 template <int N>
    343 struct NativePropertiesN {
    344  // Duo structs are stored in the duos[] array, and each element in the array
    345  // could require a different T. Therefore, we can't use the correct type for
    346  // mPrefables. Instead we use void* and cast to the correct type in the
    347  // getters.
    348  struct Duo {
    349    const /*Prefable<const T>*/ void* const mPrefables;
    350    PropertyInfo* const mPropertyInfos;
    351  };
    352 
    353  constexpr const NativePropertiesN<7>* Upcast() const {
    354    return reinterpret_cast<const NativePropertiesN<7>*>(this);
    355  }
    356 
    357  const PropertyInfo* PropertyInfos() const { return duos[0].mPropertyInfos; }
    358 
    359 #define DO(SpecT, FieldName)                                                 \
    360 public:                                                                     \
    361  /* The bitfields indicating the duo's presence and (if present) offset. */ \
    362  const uint32_t mHas##FieldName##s : 1;                                     \
    363  const uint32_t m##FieldName##sOffset : 3;                                  \
    364                                                                             \
    365 private:                                                                    \
    366  const Duo* FieldName##sDuo() const {                                       \
    367    MOZ_ASSERT(Has##FieldName##s());                                         \
    368    return &duos[m##FieldName##sOffset];                                     \
    369  }                                                                          \
    370                                                                             \
    371 public:                                                                     \
    372  bool Has##FieldName##s() const { return mHas##FieldName##s; }              \
    373  const Prefable<const SpecT>* FieldName##s() const {                        \
    374    return static_cast<const Prefable<const SpecT>*>(                        \
    375        FieldName##sDuo()->mPrefables);                                      \
    376  }                                                                          \
    377  PropertyInfo* FieldName##PropertyInfos() const {                           \
    378    return FieldName##sDuo()->mPropertyInfos;                                \
    379  }
    380 
    381  DO(JSFunctionSpec, StaticMethod)
    382  DO(JSPropertySpec, StaticAttribute)
    383  DO(JSFunctionSpec, Method)
    384  DO(JSPropertySpec, Attribute)
    385  DO(JSFunctionSpec, UnforgeableMethod)
    386  DO(JSPropertySpec, UnforgeableAttribute)
    387  DO(ConstantSpec, Constant)
    388 
    389 #undef DO
    390 
    391  // The index to the iterator method in MethodPropertyInfos() array.
    392  const int16_t iteratorAliasMethodIndex;
    393  // The number of PropertyInfo structs that the duos manage. This is the total
    394  // count across all duos.
    395  const uint16_t propertyInfoCount;
    396  // The sorted indices array from sorting property ids, which will be used when
    397  // we binary search for a property.
    398  uint16_t* sortedPropertyIndices;
    399 
    400  const Duo duos[N];
    401 };
    402 
    403 // Ensure the struct has the expected size. The 8 is for the bitfields plus
    404 // iteratorAliasMethodIndex and idsLength; the rest is for the idsSortedIndex,
    405 // and duos[].
    406 static_assert(sizeof(NativePropertiesN<1>) == 8 + 3 * sizeof(void*), "1 size");
    407 static_assert(sizeof(NativePropertiesN<2>) == 8 + 5 * sizeof(void*), "2 size");
    408 static_assert(sizeof(NativePropertiesN<3>) == 8 + 7 * sizeof(void*), "3 size");
    409 static_assert(sizeof(NativePropertiesN<4>) == 8 + 9 * sizeof(void*), "4 size");
    410 static_assert(sizeof(NativePropertiesN<5>) == 8 + 11 * sizeof(void*), "5 size");
    411 static_assert(sizeof(NativePropertiesN<6>) == 8 + 13 * sizeof(void*), "6 size");
    412 static_assert(sizeof(NativePropertiesN<7>) == 8 + 15 * sizeof(void*), "7 size");
    413 
    414 // The "base" type.
    415 typedef NativePropertiesN<7> NativeProperties;
    416 
    417 struct NativePropertiesHolder {
    418  const NativeProperties* regular;
    419  const NativeProperties* chromeOnly;
    420  // Points to a static bool that's set to true once the regular and chromeOnly
    421  // NativeProperties have been inited. This is a pointer to a bool instead of
    422  // a bool value because NativePropertiesHolder is stored by value in
    423  // a static const NativePropertyHooks.
    424  bool* inited;
    425 };
    426 
    427 struct NativeNamedOrIndexedPropertyHooks {
    428  // The hook to call for resolving indexed or named properties.
    429  ResolveOwnProperty mResolveOwnProperty;
    430  // The hook to call for enumerating indexed or named properties.
    431  EnumerateOwnProperties mEnumerateOwnProperties;
    432  // The hook to call to delete a named property.  May be null if there are no
    433  // named properties or no named property deleter.  On success (true return)
    434  // the "found" argument will be set to true if there was in fact such a named
    435  // property and false otherwise.  If it's set to false, the caller is expected
    436  // to proceed with whatever deletion behavior it would have if there were no
    437  // named properties involved at all (i.e. if the hook were null).  If it's set
    438  // to true, it will indicate via opresult whether the delete actually
    439  // succeeded.
    440  DeleteNamedProperty mDeleteNamedProperty;
    441 };
    442 
    443 // Helper structure for Xrays for DOM binding objects. The same instance is used
    444 // for instances, interface objects and interface prototype objects of a
    445 // specific interface.
    446 struct NativePropertyHooks {
    447  const NativeNamedOrIndexedPropertyHooks* mIndexedOrNamedNativeProperties;
    448 
    449  // The property arrays for this interface.
    450  NativePropertiesHolder mNativeProperties;
    451 
    452  // This will be set to the ID of the interface prototype object for the
    453  // interface, if it has one. If it doesn't have one it will be set to
    454  // prototypes::id::_ID_Count.
    455  prototypes::ID mPrototypeID;
    456 
    457  // This will be set to the ID of the interface object for the interface, if it
    458  // has one. If it doesn't have one it will be set to
    459  // constructors::id::_ID_Count.
    460  constructors::ID mConstructorID;
    461 
    462  // The JSClass to use for expandos on our Xrays.  Can be null, in which case
    463  // Xrays will use a default class of their choice.
    464  const JSClass* mXrayExpandoClass;
    465 };
    466 
    467 enum DOMObjectType : uint8_t {
    468  eInstance,
    469  eGlobalInstance,
    470  eInterface,
    471  eInterfacePrototype,
    472  eGlobalInterfacePrototype,
    473  eNamespace,
    474  eNamedPropertiesObject
    475 };
    476 
    477 inline bool IsInstance(DOMObjectType type) {
    478  return type == eInstance || type == eGlobalInstance;
    479 }
    480 
    481 inline bool IsInterfacePrototype(DOMObjectType type) {
    482  return type == eInterfacePrototype || type == eGlobalInterfacePrototype;
    483 }
    484 
    485 typedef JSObject* (*AssociatedGlobalGetter)(JSContext* aCx,
    486                                            JS::Handle<JSObject*> aObj);
    487 
    488 typedef JSObject* (*ProtoGetter)(JSContext* aCx);
    489 
    490 /**
    491 * Returns a handle to the relevant WebIDL prototype object for the current
    492 * compartment global (which may be a handle to null on out of memory).  Once
    493 * allocated, the prototype object is guaranteed to exist as long as the global
    494 * does, since the global traces its array of WebIDL prototypes and
    495 * constructors.
    496 */
    497 typedef JS::Handle<JSObject*> (*ProtoHandleGetter)(JSContext* aCx);
    498 
    499 /**
    500 * Serializes a WebIDL object for structured cloning.  aObj may not be in the
    501 * compartment of aCx in cases when we were working with a cross-compartment
    502 * wrapper.  aObj is expected to be an object of the DOMJSClass that we got the
    503 * serializer from.
    504 */
    505 typedef bool (*WebIDLSerializer)(JSContext* aCx,
    506                                 JSStructuredCloneWriter* aWriter,
    507                                 JS::Handle<JSObject*> aObj);
    508 
    509 /**
    510 * Deserializes a WebIDL object from a structured clone serialization.
    511 */
    512 typedef JSObject* (*WebIDLDeserializer)(JSContext* aCx,
    513                                        nsIGlobalObject* aGlobal,
    514                                        JSStructuredCloneReader* aReader);
    515 
    516 using WrapperCacheGetter = nsWrapperCache* (*)(JSObject*);
    517 
    518 // Special JSClass for reflected DOM objects.
    519 struct DOMJSClass {
    520  // It would be nice to just inherit from JSClass, but that precludes pure
    521  // compile-time initialization of the form |DOMJSClass = {...};|, since C++
    522  // only allows brace initialization for aggregate/POD types.
    523  const JSClass mBase;
    524 
    525  // A list of interfaces that this object implements, in order of decreasing
    526  // derivedness.
    527  const prototypes::ID mInterfaceChain[MAX_PROTOTYPE_CHAIN_LENGTH];
    528 
    529  // We store the DOM object in reserved slot with index DOM_OBJECT_SLOT or in
    530  // the proxy private if we use a proxy object.
    531  // Sometimes it's an nsISupports and sometimes it's not; this class tells
    532  // us which it is.
    533  const bool mDOMObjectIsISupports;
    534 
    535  const NativePropertyHooks* mNativeHooks;
    536 
    537  // A callback to find the associated global for our C++ object.  Note that
    538  // this is used in cases when that global is _changing_, so it will not match
    539  // the global of the JSObject* passed in to this function!
    540  AssociatedGlobalGetter mGetAssociatedGlobal;
    541  ProtoHandleGetter mGetProto;
    542 
    543  // This stores the CC participant for the native, null if this class does not
    544  // implement cycle collection or if it inherits from nsISupports (we can get
    545  // the CC participant by QI'ing in that case).
    546  nsCycleCollectionParticipant* mParticipant;
    547 
    548  // The serializer for this class if the relevant object is [Serializable].
    549  // Null otherwise.
    550  WebIDLSerializer mSerializer;
    551 
    552  // A callback to get the wrapper cache for C++ objects that don't inherit from
    553  // nsISupports, or null.
    554  WrapperCacheGetter mWrapperCacheGetter;
    555 
    556  static const DOMJSClass* FromJSClass(const JSClass* base) {
    557    MOZ_ASSERT(base->flags & JSCLASS_IS_DOMJSCLASS);
    558    return reinterpret_cast<const DOMJSClass*>(base);
    559  }
    560 
    561  const JSClass* ToJSClass() const { return &mBase; }
    562 };
    563 
    564 // Special JSClass for DOM interface and interface prototype objects.
    565 struct DOMIfaceAndProtoJSClass {
    566  // It would be nice to just inherit from JSClass, but that precludes pure
    567  // compile-time initialization of the form
    568  // |DOMJSInterfaceAndPrototypeClass = {...};|, since C++ only allows brace
    569  // initialization for aggregate/POD types.
    570  const JSClass mBase;
    571 
    572  // Either eNamespace, eInterfacePrototype,
    573  // eGlobalInterfacePrototype or eNamedPropertiesObject.
    574  DOMObjectType mType;  // uint8_t
    575 
    576  const prototypes::ID mPrototypeID;  // uint16_t
    577  const uint32_t mDepth;
    578 
    579  const NativePropertyHooks* mNativeHooks;
    580 
    581  ProtoGetter mGetParentProto;
    582 
    583  static const DOMIfaceAndProtoJSClass* FromJSClass(const JSClass* base) {
    584    MOZ_ASSERT(base->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS);
    585    return reinterpret_cast<const DOMIfaceAndProtoJSClass*>(base);
    586  }
    587 
    588  const JSClass* ToJSClass() const { return &mBase; }
    589 };
    590 
    591 class ProtoAndIfaceCache;
    592 
    593 inline bool DOMGlobalHasProtoAndIFaceCache(JSObject* global) {
    594  MOZ_DIAGNOSTIC_ASSERT(JS::GetClass(global)->flags & JSCLASS_DOM_GLOBAL);
    595  // This can be undefined if we GC while creating the global
    596  return !JS::GetReservedSlot(global, DOM_PROTOTYPE_SLOT).isUndefined();
    597 }
    598 
    599 inline bool HasProtoAndIfaceCache(JSObject* global) {
    600  if (!(JS::GetClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
    601    return false;
    602  }
    603  return DOMGlobalHasProtoAndIFaceCache(global);
    604 }
    605 
    606 inline ProtoAndIfaceCache* GetProtoAndIfaceCache(JSObject* global) {
    607  MOZ_DIAGNOSTIC_ASSERT(JS::GetClass(global)->flags & JSCLASS_DOM_GLOBAL);
    608  return static_cast<ProtoAndIfaceCache*>(
    609      JS::GetReservedSlot(global, DOM_PROTOTYPE_SLOT).toPrivate());
    610 }
    611 
    612 }  // namespace mozilla::dom
    613 
    614 #endif /* mozilla_dom_DOMJSClass_h */